
package com.ebrg.secure9.activity;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.james.mime4j.codec.EncoderUtil;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.util.encoders.Base64;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.provider.OpenableColumns;
import android.text.InputType;
import android.text.TextWatcher;
import android.text.util.Rfc822Tokenizer;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.MultiAutoCompleteTextView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AutoCompleteTextView.Validator;

import com.ebrg.secure9.Account;
import com.ebrg.secure9.EmailAddressAdapter;
import com.ebrg.secure9.EmailAddressValidator;
import com.ebrg.secure9.Identity;
import com.ebrg.secure9.Secure9;
import com.ebrg.secure9.Preferences;
import com.ebrg.secure9.activity.setup.EncryptionNewKey;
import com.ebrg.secure9.controller.MessagingController;
import com.ebrg.secure9.controller.MessagingListener;
import com.ebrg.secure9.helper.Utility;
import com.ebrg.secure9.mail.Address;
import com.ebrg.secure9.mail.Body;
import com.ebrg.secure9.mail.Flag;
import com.ebrg.secure9.mail.Message;
import com.ebrg.secure9.mail.MessagingException;
import com.ebrg.secure9.mail.Multipart;
import com.ebrg.secure9.mail.Part;
import com.ebrg.secure9.mail.Message.RecipientType;
import com.ebrg.secure9.mail.internet.MimeBodyPart;
import com.ebrg.secure9.mail.internet.MimeHeader;
import com.ebrg.secure9.mail.internet.MimeMessage;
import com.ebrg.secure9.mail.internet.MimeMultipart;
import com.ebrg.secure9.mail.internet.MimeUtility;
import com.ebrg.secure9.mail.internet.TextBody;
import com.ebrg.secure9.mail.security.DiffieHellman;
import com.ebrg.secure9.mail.security.Encryptor;
import com.ebrg.secure9.mail.security.Hash;
import com.ebrg.secure9.mail.security.Keys;
import com.ebrg.secure9.mail.store.LocalStore;
import com.ebrg.secure9.mail.store.LocalStore.LocalAttachmentBody;
import com.ebrg.secure9.R;


public class MessageCompose extends K9Activity implements OnClickListener, OnFocusChangeListener
{
	private static final String ACTION_REPLY = "com.ebrg.secure9.intent.action.REPLY";
	private static final String ACTION_REPLY_ALL = "com.ebrg.secure9.intent.action.REPLY_ALL";
	private static final String ACTION_FORWARD = "com.ebrg.secure9.intent.action.FORWARD";
	private static final String ACTION_EDIT_DRAFT = "com.ebrg.secure9.intent.action.EDIT_DRAFT";


	private static final String EXTRA_ACCOUNT = "account";
	private static final String EXTRA_FOLDER = "folder";
	private static final String EXTRA_MESSAGE = "message";


	private static final String STATE_KEY_ATTACHMENTS =
		"com.ebrg.secure9.activity.MessageCompose.attachments";
	private static final String STATE_KEY_CC_SHOWN =
		"com.ebrg.secure9.activity.MessageCompose.ccShown";
	private static final String STATE_KEY_BCC_SHOWN =
		"com.ebrg.secure9.activity.MessageCompose.bccShown";
	private static final String STATE_KEY_QUOTED_TEXT_SHOWN =
		"com.ebrg.secure9.activity.MessageCompose.quotedTextShown";
	private static final String STATE_KEY_SOURCE_MESSAGE_PROCED =
		"com.ebrg.secure9.activity.MessageCompose.stateKeySourceMessageProced";
	private static final String STATE_KEY_DRAFT_UID =
		"com.ebrg.secure9.activity.MessageCompose.draftUid";
	private static final String STATE_IDENTITY_CHANGED =
		"com.ebrg.secure9.activity.MessageCompose.identityChanged";
	private static final String STATE_IDENTITY =
		"com.ebrg.secure9.activity.MessageCompose.identity";

	private static final int MSG_PROGRESS_ON = 1;
	private static final int MSG_PROGRESS_OFF = 2;
	private static final int MSG_UPDATE_TITLE = 3;
	private static final int MSG_SKIPPED_ATTACHMENTS = 4;
	private static final int MSG_SAVED_DRAFT = 5;
	private static final int MSG_DISCARDED_DRAFT = 6;

	private static final int ACTIVITY_REQUEST_PICK_ATTACHMENT = 1;
	private static final int ACTIVITY_CHOOSE_IDENTITY = 2;

	private Account mAccount;
	private Identity mIdentity;
	private boolean mIdentityChanged = false;
	private boolean mSignatureChanged = false;
	private String mFolder;
	private String mSourceMessageUid;
	private Message mSourceMessage;
	private String tempSignature;
	/**
	 * Indicates that the source message has been processed at least once and should not
	 * be processed on any subsequent loads. This protects us from adding attachments that
	 * have already been added from the restore of the view state.
	 */
	private boolean mSourceMessageProcessed = false;


	private TextView mFromView;
	private MultiAutoCompleteTextView mToView;
	private MultiAutoCompleteTextView mCcView;
	private MultiAutoCompleteTextView mBccView;
	private EditText mSubjectView;
	private EditText mSignatureView;
	private EditText mMessageContentView;
	private LinearLayout mAttachments;
	private View mQuotedTextBar;
	private ImageButton mQuotedTextDelete;
	private EditText mQuotedText;

	private String mReferences;
	private String mInReplyTo;
	private boolean mDraftNeedsSaving = false;

	////Secure9
	private boolean isEncrypted = false;
	private static String recipient;
	private boolean autoSend; //If encryptAll option is selected, autosend on completion of Encryption. 
	private boolean hasAttachments = false;
	private Menu optionsMenu;

	/**
	 * The draft uid of this message. This is used when saving drafts so that the same draft is
	 * overwritten instead of being created anew. This property is null until the first save.
	 */
	private String mDraftUid;

	private Handler mHandler = new Handler()
	{
		@Override
		public void handleMessage(android.os.Message msg)
		{
			switch (msg.what)
			{
			case MSG_PROGRESS_ON:
				setProgressBarIndeterminateVisibility(true);
				break;
			case MSG_PROGRESS_OFF:
				setProgressBarIndeterminateVisibility(false);
				break;
			case MSG_UPDATE_TITLE:
				updateTitle();
				break;
			case MSG_SKIPPED_ATTACHMENTS:
				Toast.makeText(
						MessageCompose.this,
						getString(R.string.message_compose_attachments_skipped_toast),
						Toast.LENGTH_LONG).show();
				break;
			case MSG_SAVED_DRAFT:
				Toast.makeText(
						MessageCompose.this,
						getString(R.string.message_saved_toast),
						Toast.LENGTH_LONG).show();
				break;
			case MSG_DISCARDED_DRAFT:
				Toast.makeText(
						MessageCompose.this,
						getString(R.string.message_discarded_toast),
						Toast.LENGTH_LONG).show();
				break;
			default:
				super.handleMessage(msg);
				break;
			}
		}
	};

	private Listener mListener = new Listener();
	private EmailAddressAdapter mAddressAdapter;
	private Validator mAddressValidator;


	class Attachment implements Serializable
	{
		public String name;
		public String contentType;
		public long size;
		public Uri uri;
	}

	/**
	 * Compose a new message using the given account. If account is null the default account
	 * will be used.
	 * @param context
	 * @param account
	 */
	public static void actionCompose(Context context, Account account)
	{
		if (account == null)
		{
			account = Preferences.getPreferences(context).getDefaultAccount();
		}
		Intent i = new Intent(context, MessageCompose.class);
		i.putExtra(EXTRA_ACCOUNT, account.getUuid());
		context.startActivity(i);
	}

	/**
	 * Compose a new message as a reply to the given message. If replyAll is true the function
	 * is reply all instead of simply reply.
	 * @param context
	 * @param account
	 * @param message
	 * @param replyAll
	 */
	public static void actionReply(
			Context context,
			Account account,
			Message message,
			boolean replyAll)
	{
		Intent i = new Intent(context, MessageCompose.class);
		i.putExtra(EXTRA_ACCOUNT, account.getUuid());
		i.putExtra(EXTRA_FOLDER, message.getFolder().getName());
		i.putExtra(EXTRA_MESSAGE, message.getUid());
		if (replyAll)
		{
			i.setAction(ACTION_REPLY_ALL);
		}
		else
		{
			i.setAction(ACTION_REPLY);
		}
		context.startActivity(i);
	}

	/**
	 * Compose a new message as a forward of the given message.
	 * @param context
	 * @param account
	 * @param message
	 */
	public static void actionForward(Context context, Account account, Message message)
	{
		Intent i = new Intent(context, MessageCompose.class);
		i.putExtra(EXTRA_ACCOUNT, account.getUuid());
		i.putExtra(EXTRA_FOLDER, message.getFolder().getName());
		i.putExtra(EXTRA_MESSAGE, message.getUid());
		i.setAction(ACTION_FORWARD);
		context.startActivity(i);
	}

	/**
	 * Continue composition of the given message. This action modifies the way this Activity
	 * handles certain actions.
	 * Save will attempt to replace the message in the given folder with the updated version.
	 * Discard will delete the message from the given folder.
	 * @param context
	 * @param account
	 * @param folder
	 * @param message
	 */
	public static void actionEditDraft(Context context, Account account, Message message)
	{
		Intent i = new Intent(context, MessageCompose.class);
		i.putExtra(EXTRA_ACCOUNT, account.getUuid());
		i.putExtra(EXTRA_FOLDER, message.getFolder().getName());
		i.putExtra(EXTRA_MESSAGE, message.getUid());
		i.setAction(ACTION_EDIT_DRAFT);
		context.startActivity(i);
	}

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);

		requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);


		setContentView(R.layout.message_compose);

		Intent intent = getIntent();
		String accountUuid = intent.getStringExtra(EXTRA_ACCOUNT);
		mAccount = Preferences.getPreferences(this).getAccount(accountUuid);

		if (mAccount == null)
		{
			mAccount = Preferences.getPreferences(this).getDefaultAccount();
		}
		if (mAccount == null)
		{
			/*
			 * There are no accounts set up. This should not have happened. Prompt the
			 * user to set up an account as an acceptable bailout.
			 */
			startActivity(new Intent(this, Accounts.class));
			mDraftNeedsSaving = false;
			finish();
			return;
		}


		mAddressAdapter = EmailAddressAdapter.getInstance(this);
		mAddressValidator = new EmailAddressValidator();

		mFromView = (TextView)findViewById(R.id.from);
		mToView = (MultiAutoCompleteTextView)findViewById(R.id.to);
		mCcView = (MultiAutoCompleteTextView)findViewById(R.id.cc);
		mBccView = (MultiAutoCompleteTextView)findViewById(R.id.bcc);
		mSubjectView = (EditText)findViewById(R.id.subject);

		EditText upperSignature = (EditText)findViewById(R.id.upper_signature);
		EditText lowerSignature = (EditText)findViewById(R.id.lower_signature);

		mMessageContentView = (EditText)findViewById(R.id.message_content);
		mAttachments = (LinearLayout)findViewById(R.id.attachments);
		mQuotedTextBar = findViewById(R.id.quoted_text_bar);
		mQuotedTextDelete = (ImageButton)findViewById(R.id.quoted_text_delete);
		mQuotedText = (EditText)findViewById(R.id.quoted_text);

		TextWatcher watcher = new TextWatcher()
		{
			public void beforeTextChanged(CharSequence s, int start,
					int before, int after) { }

			public void onTextChanged(CharSequence s, int start,
					int before, int count)
			{
				mDraftNeedsSaving = true;
			}

			public void afterTextChanged(android.text.Editable s) { }
		};

		TextWatcher sigwatcher = new TextWatcher()
		{
			public void beforeTextChanged(CharSequence s, int start,
					int before, int after) { }

			public void onTextChanged(CharSequence s, int start,
					int before, int count)
			{
				mDraftNeedsSaving = true;
				mSignatureChanged = true;
			}

			public void afterTextChanged(android.text.Editable s) { }
		};

		mToView.addTextChangedListener(watcher);
		mCcView.addTextChangedListener(watcher);
		mBccView.addTextChangedListener(watcher);
		mSubjectView.addTextChangedListener(watcher);

		mMessageContentView.addTextChangedListener(watcher);
		mQuotedText.addTextChangedListener(watcher);

		/*
		 * We set this to invisible by default. Other methods will turn it back on if it's
		 * needed.
		 */
		mQuotedTextBar.setVisibility(View.GONE);
		mQuotedText.setVisibility(View.GONE);

		mQuotedTextDelete.setOnClickListener(this);

		mFromView.setVisibility(View.GONE);

		mToView.setAdapter(mAddressAdapter);
		mToView.setTokenizer(new Rfc822Tokenizer());
		mToView.setValidator(mAddressValidator);

		mCcView.setAdapter(mAddressAdapter);
		mCcView.setTokenizer(new Rfc822Tokenizer());
		mCcView.setValidator(mAddressValidator);

		mBccView.setAdapter(mAddressAdapter);
		mBccView.setTokenizer(new Rfc822Tokenizer());
		mBccView.setValidator(mAddressValidator);


		mSubjectView.setOnFocusChangeListener(this);

		if (savedInstanceState != null)
		{
			/*
			 * This data gets used in onCreate, so grab it here instead of onRestoreIntstanceState
			 */
			mSourceMessageProcessed = savedInstanceState.getBoolean(STATE_KEY_SOURCE_MESSAGE_PROCED, false);
		}


		String action = intent.getAction();

		if (Intent.ACTION_VIEW.equals(action) || Intent.ACTION_SENDTO.equals(action))
		{
			/*
			 * Someone has clicked a mailto: link. The address is in the URI.
			 */
			if (intent.getData() != null)
			{
				Uri uri = intent.getData();
				if ("mailto".equals(uri.getScheme()))
				{
					initializeFromMailTo(uri.toString());
				}
				else
				{
					String toText = uri.getSchemeSpecificPart();
					if (toText != null)
					{
						mToView.setText(toText);
					}
				}
			}
		}
		//TODO: Use constant Intent.ACTION_SEND_MULTIPLE once we drop Android 1.5 support
		else if (Intent.ACTION_SEND.equals(action)
				|| Intent.ACTION_SENDTO.equals(action)
				|| "android.intent.action.SEND_MULTIPLE".equals(action))
		{
			/*
			 * Someone is trying to compose an email with an attachment, probably Pictures.
			 * The Intent should contain an EXTRA_STREAM with the data to attach.
			 */

			String text = intent.getStringExtra(Intent.EXTRA_TEXT);
			if (text != null)
			{
				mMessageContentView.setText(text + "CONTENT");
			}
			String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
			if (subject != null)
			{
				mSubjectView.setText(subject + "HAHA");
			}

			String type = intent.getType();
			//TODO: Use constant Intent.ACTION_SEND_MULTIPLE once we drop Android 1.5 support
			if ("android.intent.action.SEND_MULTIPLE".equals(action))
			{
				ArrayList<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
				if (list != null)
				{
					for (Parcelable parcelable : list)
					{
						Uri stream = (Uri) parcelable;
						if (stream != null && type != null)
						{
							if (MimeUtility.mimeTypeMatches(type, Secure9.ACCEPTABLE_ATTACHMENT_SEND_TYPES))
							{
								addAttachment(stream);
							}
						}
					}
				}
			}
			else
			{
				Uri stream = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
				if (stream != null && type != null)
				{
					if (MimeUtility.mimeTypeMatches(type, Secure9.ACCEPTABLE_ATTACHMENT_SEND_TYPES))
					{
						addAttachment(stream);
					}
				}
			}

			/*
			 * There might be an EXTRA_SUBJECT, EXTRA_TEXT, EXTRA_EMAIL, EXTRA_BCC or EXTRA_CC
			 */

			String extraSubject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
			String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);

			mSubjectView.setText(extraSubject);
			mMessageContentView.setText(extraText);

			String[] extraEmail = intent.getStringArrayExtra(Intent.EXTRA_EMAIL);
			String[] extraCc = intent.getStringArrayExtra(Intent.EXTRA_CC);
			String[] extraBcc = intent.getStringArrayExtra(Intent.EXTRA_BCC);

			String addressList;
			// Cache array size, as per Google's recommendations.
			int arraySize;
			int i;

			addressList = "";
			if (extraEmail != null)
			{
				arraySize = extraEmail.length;
				for (i=0; i < arraySize; i++)
				{
					addressList += extraEmail[i]+", ";
				}
			}
			mToView.setText(addressList);

			addressList = "";
			if (extraCc != null)
			{
				arraySize = extraCc.length;
				for (i=0; i < arraySize; i++)
				{
					addressList += extraCc[i]+", ";
				}
			}
			mCcView.setText(addressList);

			addressList = "";
			if (extraBcc != null)
			{
				arraySize = extraBcc.length;
				for (i=0; i < arraySize; i++)
				{
					addressList += extraBcc[i]+", ";
				}
			}
			mBccView.setText(addressList);

		}
		else
		{
			mFolder = (String) intent.getStringExtra(EXTRA_FOLDER);
			mSourceMessageUid = (String) intent.getStringExtra(EXTRA_MESSAGE);
		}

		if (mIdentity == null)
		{
			mIdentity = mAccount.getIdentity(0);
		}

		if (mAccount.isSignatureBeforeQuotedText())
		{
			mSignatureView = upperSignature;
			lowerSignature.setVisibility(View.GONE);
		}
		else
		{
			mSignatureView = lowerSignature;
			upperSignature.setVisibility(View.GONE);
		}
		mSignatureView.addTextChangedListener(sigwatcher);

		if (!mIdentity.getSignatureUse())
		{
			mSignatureView.setVisibility(View.GONE);
		}

		if (!mSourceMessageProcessed)
		{
			updateFrom();
			updateSignature();

			if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action) || ACTION_FORWARD.equals(action) || ACTION_EDIT_DRAFT.equals(action))
			{
				/*
				 * If we need to load the message we add ourself as a message listener here
				 * so we can kick it off. Normally we add in onResume but we don't
				 * want to reload the message every time the activity is resumed.
				 * There is no harm in adding twice.
				 */
				MessagingController.getInstance(getApplication()).addListener(mListener);
				MessagingController.getInstance(getApplication()).loadMessageForView(mAccount, mFolder, mSourceMessageUid, null);
			}

			if (!ACTION_EDIT_DRAFT.equals(action))
			{
				String bccAddress = mAccount.getAlwaysBcc();
				if (bccAddress!=null
						&& !"".equals(bccAddress))
				{
					addAddress(mBccView, new Address(mAccount.getAlwaysBcc(), ""));
				}
			}

			if (Secure9.DEBUG)
				Log.d(Secure9.LOG_TAG, "action = " + action + ", mAccount = " + mAccount + ", mFolder = " + mFolder + ", mSourceMessageUid = " + mSourceMessageUid);
			if ((ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action)) && mAccount != null && mFolder != null && mSourceMessageUid != null)
			{
				if (Secure9.DEBUG)
					Log.d(Secure9.LOG_TAG, "Setting message ANSWERED flag to true");
				// TODO: Really, we should wait until we send the message, but that would require saving the original
				// message info along with a Draft copy, in case it is left in Drafts for a while before being sent
				MessagingController.getInstance(getApplication()).setFlag(mAccount, mFolder, new String[] { mSourceMessageUid }, Flag.ANSWERED, true);
			}

			updateTitle();
		}

		if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action) || ACTION_EDIT_DRAFT.equals(action))
		{
			//change focus to message body.
			mMessageContentView.requestFocus();
		}

		mDraftNeedsSaving = false;


	}

	@Override
	public void onResume()
	{
		super.onResume();
		MessagingController.getInstance(getApplication()).addListener(mListener);
	}

	@Override
	public void onPause()
	{
		super.onPause();
		saveIfNeeded();
		MessagingController.getInstance(getApplication()).removeListener(mListener);
	}

	/**
	 * The framework handles most of the fields, but we need to handle stuff that we
	 * dynamically show and hide:
	 * Attachment list,
	 * Cc field,
	 * Bcc field,
	 * Quoted text,
	 */
	@Override
	protected void onSaveInstanceState(Bundle outState)
	{
		super.onSaveInstanceState(outState);
		saveIfNeeded();
		ArrayList<Uri> attachments = new ArrayList<Uri>();
		for (int i = 0, count = mAttachments.getChildCount(); i < count; i++)
		{
			View view = mAttachments.getChildAt(i);
			Attachment attachment = (Attachment) view.getTag();
			attachments.add(attachment.uri);
		}
		outState.putParcelableArrayList(STATE_KEY_ATTACHMENTS, attachments);
		outState.putBoolean(STATE_KEY_CC_SHOWN, mCcView.getVisibility() == View.VISIBLE);
		outState.putBoolean(STATE_KEY_BCC_SHOWN, mBccView.getVisibility() == View.VISIBLE);
		outState.putBoolean(STATE_KEY_QUOTED_TEXT_SHOWN, mQuotedTextBar.getVisibility() == View.VISIBLE);
		outState.putBoolean(STATE_KEY_SOURCE_MESSAGE_PROCED, mSourceMessageProcessed);
		outState.putString(STATE_KEY_DRAFT_UID, mDraftUid);
		outState.putSerializable(STATE_IDENTITY, mIdentity);
		outState.putBoolean(STATE_IDENTITY_CHANGED, mIdentityChanged);
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState)
	{
		super.onRestoreInstanceState(savedInstanceState);
		ArrayList<Parcelable> attachments = (ArrayList<Parcelable>) savedInstanceState.getParcelableArrayList(STATE_KEY_ATTACHMENTS);
		mAttachments.removeAllViews();
		for (Parcelable p : attachments)
		{
			Uri uri = (Uri) p;
			addAttachment(uri);
		}

		mCcView.setVisibility(savedInstanceState.getBoolean(STATE_KEY_CC_SHOWN) ?  View.VISIBLE : View.GONE);
		mBccView.setVisibility(savedInstanceState.getBoolean(STATE_KEY_BCC_SHOWN) ?  View.VISIBLE : View.GONE);
		mQuotedTextBar.setVisibility(savedInstanceState.getBoolean(STATE_KEY_QUOTED_TEXT_SHOWN) ?  View.VISIBLE : View.GONE);
		mQuotedText.setVisibility(savedInstanceState.getBoolean(STATE_KEY_QUOTED_TEXT_SHOWN) ?  View.VISIBLE : View.GONE);
		mDraftUid = savedInstanceState.getString(STATE_KEY_DRAFT_UID);
		mIdentity = (Identity)savedInstanceState.getSerializable(STATE_IDENTITY);
		mIdentityChanged = savedInstanceState.getBoolean(STATE_IDENTITY_CHANGED);
		updateFrom();
		updateSignature();

		mDraftNeedsSaving = false;
	}

	private void updateTitle()
	{
		if (mSubjectView.getText().length() == 0)
		{
			setTitle(R.string.compose_title);
		}
		else
		{
			setTitle(mSubjectView.getText().toString());
		}
	}

	public void onFocusChange(View view, boolean focused)
	{
		if (!focused)
		{
			updateTitle();
		}
	}

	private void addAddresses(MultiAutoCompleteTextView view, Address[] addresses)
	{
		if (addresses == null)
		{
			return;
		}
		for (Address address : addresses)
		{
			addAddress(view, address);
		}
	}

	private void addAddress(MultiAutoCompleteTextView view, Address address)
	{
		view.append(address + ", ");
	}

	private Address[] getAddresses(MultiAutoCompleteTextView view)
	{
		Address[] addresses = Address.parseUnencoded(view.getText().toString().trim());
		return addresses;
	}

	//Secure9 for key setup
	public static Address[] getAddressFromString(String email)
	{
		Address[] addresses = Address.parseUnencoded(email.trim());

		return addresses;
	}
	private MimeMessage createMessage(boolean appendSig) throws MessagingException
	{
		MimeMessage message = new MimeMessage();
		message.addSentDate(new Date());
		Address from = new Address(mIdentity.getEmail(), mIdentity.getName());
		message.setFrom(from);
		message.setRecipients(RecipientType.TO, getAddresses(mToView));
		message.setRecipients(RecipientType.CC, getAddresses(mCcView));
		message.setRecipients(RecipientType.BCC, getAddresses(mBccView));
		message.setSubject(mSubjectView.getText().toString());
		message.setHeader("X-User-Agent", getString(R.string.message_header_mua));

		final String replyTo = mIdentity.getReplyTo();
		if (replyTo != null)
		{
			message.setReplyTo(new Address[] { new Address(replyTo) });
		}

		if (mInReplyTo != null)
		{
			message.setInReplyTo(mInReplyTo);
		}

		if (mReferences != null)
		{
			message.setReferences(mReferences);
		}


		/*
		 * Build the Body that will contain the text of the message. We'll decide where to
		 * include it later.
		 */

		String text = mMessageContentView.getText().toString();

		//TODO: Encrypt Message Here!!!!!
		if (appendSig && mAccount.isSignatureBeforeQuotedText())
		{
			text = appendSignature(text);
		}

		if (mQuotedTextBar.getVisibility() == View.VISIBLE)
		{
			text += "\n" + mQuotedText.getText().toString();
		}


		if (appendSig && mAccount.isSignatureBeforeQuotedText() == false)
		{
			text = appendSignature(text);
		}


		TextBody body = new TextBody("\r\n" + text + " --");

		if (mAttachments.getChildCount() > 0)
		{
			/*
			 * The message has attachments that need to be included. First we add the part
			 * containing the text that will be sent and then we include each attachment.
			 */

			MimeMultipart mp;

			mp = new MimeMultipart();
			mp.addBodyPart(new MimeBodyPart(body, "text/plain"));

			for (int i = 0, count = mAttachments.getChildCount(); i < count; i++)
			{
				Attachment attachment = (Attachment) mAttachments.getChildAt(i).getTag();

				MimeBodyPart bp = new MimeBodyPart(
						new LocalStore.LocalAttachmentBody(attachment.uri, getApplication()));

				/*
				 * Correctly encode the filename here. Otherwise the whole
				 * header value (all parameters at once) will be encoded by
				 * MimeHeader.writeTo().
				 */
				bp.addHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n name=\"%s\"",
						attachment.contentType,
						EncoderUtil.encodeIfNecessary(attachment.name,
								EncoderUtil.Usage.WORD_ENTITY, 7)));

				bp.addHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");

				/*
				 * TODO: Oh the joys of MIME...
				 *
				 * From RFC 2183 (The Content-Disposition Header Field):
				 * "Parameter values longer than 78 characters, or which
				 *  contain non-ASCII characters, MUST be encoded as specified
				 *  in [RFC 2184]."
				 *
				 * Example:
				 *
				 * Content-Type: application/x-stuff
				 *  title*1*=us-ascii'en'This%20is%20even%20more%20
				 *  title*2*=%2A%2A%2Afun%2A%2A%2A%20
				 *  title*3="isn't it!"
				 */
				bp.addHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, String.format(
						"attachment;\n filename=\"%s\";\n size=%d",
						attachment.name, attachment.size));

				mp.addBodyPart(bp);
			}

			message.setBody(mp);
		}
		else
		{
			/*
			 * No attachments to include, just stick the text body in the message and call
			 * it good.
			 */
			message.setBody(body);
		}

		return message;
	}

	/**
	 * Appends the signature to the email to be sent. 
	 * @param text
	 * @return
	 */
	private String appendSignature(String text)
	{
		if (mIdentity.getSignatureUse())
		{
			String signature = mSignatureView.getText().toString();

			if (signature != null && !signature.contentEquals(""))
			{
				text += "\n" + signature;
			}
		}

		return text;
	}

	private void sendOrSaveMessage(boolean save)
	{
		/*
		 * Create the message from all the data the user has entered.
		 */
		MimeMessage message;
		try
		{
			message = createMessage(!save);  // Only append sig on save
		}
		catch (MessagingException me)
		{
			Log.e(Secure9.LOG_TAG, "Failed to create new message for send or save.", me);
			throw new RuntimeException("Failed to create a new message for send or save.", me);
		}

		if (save)
		{
			/*
			 * Save a draft
			 */
			if (mDraftUid != null)
			{
				message.setUid(mDraftUid);
			}
			else if (ACTION_EDIT_DRAFT.equals(getIntent().getAction()))
			{
				/*
				 * We're saving a previously saved draft, so update the new message's uid
				 * to the old message's uid.
				 */
				message.setUid(mSourceMessageUid);
			}

			String secure9identity = Utility.base64Encode("" + mMessageContentView.getText().toString().length());

			if (mIdentityChanged || mSignatureChanged)
			{
				String signature  = mSignatureView.getText().toString();
				secure9identity += ":" + Utility.base64Encode(signature);
				if (mIdentityChanged)
				{

					String name = mIdentity.getName();
					String email = mIdentity.getEmail();

					secure9identity +=  ":" + Utility.base64Encode(name) + ":" + Utility.base64Encode(email);
				}
			}

			if (Secure9.DEBUG)
				Log.d(Secure9.LOG_TAG, "Saving identity: " + secure9identity);
			message.addHeader(Secure9.K9MAIL_IDENTITY, secure9identity);

			Message draftMessage = MessagingController.getInstance(getApplication()).saveDraft(mAccount, message);
			mDraftUid = draftMessage.getUid();

			// Don't display the toast if the user is just changing the orientation
			if ((getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0)
			{
				mHandler.sendEmptyMessage(MSG_SAVED_DRAFT);
			}
		}
		else
		{
			MessagingController.getInstance(getApplication()).sendMessage(mAccount, message, null);
			if (mDraftUid != null)
			{
				MessagingController.getInstance(getApplication()).deleteDraft(mAccount, mDraftUid);
				mDraftUid = null;
			}
		}
	}

	private void saveIfNeeded()
	{
		if (!mDraftNeedsSaving)
		{
			return;
		}
		mDraftNeedsSaving = false;
		sendOrSaveMessage(true);
	}

	//Secure9 - If "encrypt All" is selected in options menu,
	//This is called at the completion of Encryption().  Only called
	//if Encryption finished without error.
	private void autoSend()
	{

		if(isEncrypted==true)
		{
			if (getAddresses(mToView).length == 0 && getAddresses(mCcView).length == 0 && getAddresses(mBccView).length == 0)
			{
				mToView.setError(getString(R.string.message_compose_error_no_recipients));
				Toast.makeText(this, getString(R.string.message_compose_error_no_recipients), Toast.LENGTH_LONG).show();
				return;
			}
			sendOrSaveMessage(false);
			mDraftNeedsSaving = false;
			finish();
			Toast.makeText(getApplicationContext(), "Successefully encrypted and sent", Toast.LENGTH_LONG).show();

		}
		else
			Toast.makeText(this, "Encryption error. Message not sent.", Toast.LENGTH_LONG).show();
	}

	private void onSend()
	{
		///Secure9 - Encrypt first if autoEncrypt is enabled, then call autoSend.  Need to clean this up to only
		//use one sending method without being a security risk.
		if(mAccount.getEncryptAll() && !isEncrypted){
			autoSend = true;
			onEncrypt();
		}
		else{
			if (getAddresses(mToView).length == 0 && getAddresses(mCcView).length == 0 && getAddresses(mBccView).length == 0)
			{
				mToView.setError(getString(R.string.message_compose_error_no_recipients));
				Toast.makeText(this, getString(R.string.message_compose_error_no_recipients), Toast.LENGTH_LONG).show();
				return;
			}
			sendOrSaveMessage(false);
			mDraftNeedsSaving = false;
			finish();
			
		}

	}

	private void onDiscard()
	{
		if (mDraftUid != null)
		{
			MessagingController.getInstance(getApplication()).deleteDraft(mAccount, mDraftUid);
			mDraftUid = null;
		}
		mHandler.sendEmptyMessage(MSG_DISCARDED_DRAFT);
		mDraftNeedsSaving = false;
		finish();
	}

	private void onSave()
	{

		mDraftNeedsSaving = true;
		saveIfNeeded();
		finish();
	}

	private void onAddCcBcc()
	{
		mCcView.setVisibility(View.VISIBLE);
		mBccView.setVisibility(View.VISIBLE);
	}

	String byteToString(byte b[]) {
		String s = "";

		for(int i = 0; i<b.length; i++) {
			s += " " + b[i];
		}

		return s;
	}



	//For use by decrypt() method - Strip off everything but the ciphertext.
	private String parseMessage(String text) 
	{
		int i = 0;
		final int SUBSTRING_CUTOFF = 18;
		String str = "";
		if(text.contains("BEGIN CIPHERTEXT: ")) 
		{

			while (!text.substring(0).startsWith("BEGIN CIPHERTEXT: ")) 			
				text = text.substring(1);
			
			i = 0;
			text = text.substring(SUBSTRING_CUTOFF);

			while (!text.substring(i).startsWith(" :END CIPHERTEXT\n\n"))			
				str += text.charAt(i++);
			

			return str;
		}
		else
			return "";
	}

	//Undo Encryption (so user can edit again before sending)
	private void decrypt(char[] password) throws UnsupportedEncodingException, CryptoException{
		String message = mMessageContentView.getText().toString();		

		message = parseMessage(message);
		message = new String(Base64.decode(message.getBytes()));


		String subject = mSubjectView.getText().toString();
		mSignatureView.setText(getTempSignature());


		//decrypt Message
		String t = message;
		String[] stArr = t.split(" ");
		//byte[] key = String.valueOf(password).getBytes("UTF8");
		byte[] key = Hash.charsToBytes(password);
		Encryptor e = new Encryptor(key);

		byte[] byteMe2 = new byte[stArr.length];

		for (int i = 0; i < stArr.length; i++) 
			byteMe2[i] = Byte.parseByte(stArr[i]);

		message = e.decryptString(byteMe2);

		if(mAccount.getEncryptSubject() && 
		   !mSubjectView.getText().toString().equals(""))
		{			
			//Decrypt subject
			subject = new String(Base64.decode(subject.getBytes()));
			String s = subject;
			String[] subArr = s.split(" ");
			byte[] byteMeSubject2 = new byte[subArr.length];
			for (int i = 0; i < subArr.length; i++) 
				byteMeSubject2[i] = Byte.parseByte(subArr[i]);

			subject = e.decryptString(byteMeSubject2);
			mSubjectView.setText(subject);		
		}

		mMessageContentView.setText(this.parseTempSignature(message));		
		mToView.setEnabled(true);
		mCcView.setEnabled(true);
		mBccView.setEnabled(true); 
		mMessageContentView.setFocusableInTouchMode(true);
		mSignatureView.setFocusableInTouchMode(true);
		mSubjectView.setFocusableInTouchMode(true);
		mSignatureView.setEnabled(true);
		mSubjectView.setEnabled(true);
		mMessageContentView.setEnabled(true);	

		//Show keyboard
		((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))  
		.showSoftInput(mMessageContentView, 0);   

		isEncrypted = false;

		if(password != null)
			Arrays.fill(password, '0');


		byte[] filler;
		filler = "0".getBytes();
		Arrays.fill(key, filler[0]);

		optionsMenu.findItem(R.id.encrypt).setTitle("Encrypt").setIcon(R.drawable.ic_menu_encrypt);
	}

	/*
	 * getPassword() asks the user for a one-time Encryption password.
	 * Called by onEncrypt if no key exists for recipient.  Is passed "action" string
	 * "encrypt" or "decrypt".  Calls appropriate method/thread
	 */

	private void getPassword()
	{

		AlertDialog.Builder alert = new AlertDialog.Builder(this);
		if(isEncrypted)
		{
			alert.setTitle("Undo Encryption");
			alert.setMessage("Please enter your Encryption password to undo");
			alert.setIcon(R.drawable.decrypted);
		}
		else
		{
			alert.setTitle("Encryption password");
			alert.setMessage("Enter a 16 character Encryption password");
			alert.setIcon(R.drawable.encrypted);
		}
		// Set an EditText view to get user input 
		final EditText input = new EditText(this);
		input.setTransformationMethod
		(android.text.method.PasswordTransformationMethod.getInstance());
		alert.setView(input);

		alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() 
		{
			public void onClick(DialogInterface dialog, int whichButton) 
			{
				char[] password;
				password = new char[input.getText().length()];
				for(int i = 0; i < input.getText().length(); i++)
					password[i] = input.getText().charAt(i);

				// call encrypt action.  Still need to change password to char[]
				if(password.length == 16)
					if(!isEncrypted)
					{
						//Call encrypt
						new Encryption(password).execute();    			
					}
					else
					{
						try 
						{
							decrypt(password);					
						} 
						catch (UnsupportedEncodingException e) {Toast.makeText(getApplicationContext(), e.getLocalizedMessage(), Toast.LENGTH_LONG).show();}
						catch (CryptoException e) {Toast.makeText(getApplicationContext(), e.getLocalizedMessage(), Toast.LENGTH_LONG).show();}
					}

				else
				{
					Toast.makeText(getApplicationContext(), R.string.password_length_error, Toast.LENGTH_LONG).show();
					Arrays.fill(password, '0');
				} 
			}
		});

		alert.setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int whichButton) {
				// Canceled.

			}
		});


		alert.show();

	}


	/*
	 * Encryption thread performs the actual Encryption on the message.  This method
	 * can be called by the getPassword() method for one time Encryption password,
	 * or by the "TO DO" stored key method.
	 */
	private class Encryption extends AsyncTask<String, char[], String>
	{
		//Local variables - unsafe string handling
		private ProgressDialog pd = new ProgressDialog(MessageCompose.this);
		private String cipherMessage = "";
		private String strCipherSubject = "";
		private String strCipherSig = "";
		private String subject;
		private String sig;
		private String str;
		private char[] localPassword;



		public Encryption(char[] password)
		{
			localPassword = password;			
		}

		protected void onPreExecute()
		{   			
			//Disable editing of email addresses once message is encrypted
			mToView.setEnabled(false);
			mCcView.setEnabled(false);
			mBccView.setEnabled(false);
			mSubjectView.setEnabled(false);
			mMessageContentView.setEnabled(false);

			//Set up progress dialog
			if(mAccount.getAes256())
				pd.setMessage("Encrypting 256-bit, please wait.");
			else
				pd.setMessage("Encrypting, please wait...");
			pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
			pd.show();
			//String messageText = mMessageContentView.getText().toString();
			subject = mSubjectView.getText().toString();
			sig = mSignatureView.getText().toString();
			str = mMessageContentView.getText().toString() +"\n\n" +  
			mSignatureView.getText().toString();

			setTempSignature(sig);
			onProgressUpdate(15);
		}

		protected void onProgressUpdate(int progress)
		{
			//Set progress dialog (update)
			pd.setProgress(progress);
		}



		protected String doInBackground(String... params) 
		{
			try 
			{    
				onProgressUpdate(20);
				byte[] key = Hash.charsToBytes(localPassword);

				Encryptor e = new Encryptor(key);
				onProgressUpdate(25);
				byte[] cipherText = e.encryptString(str);
				byte[] cipherSig = e.encryptString(sig);
				onProgressUpdate(30);

				//Encrypt message
				for (int i = 0; i < cipherText.length; i++) 
					cipherMessage += Byte.toString(cipherText[i]) + " ";

				onProgressUpdate(45);

				if(mAccount.getEncryptSubject())
				{
					byte[] cipherSubject = e.encryptString(subject);
					//Encrypt subject
					for (int i = 0; i < cipherSubject.length; i++) 
						strCipherSubject += Byte.toString(cipherSubject[i]) + " ";					
					strCipherSubject= new String(Base64.encode(strCipherSubject.getBytes()));
				}

				onProgressUpdate(55);

				//Encrypt signature
				for (int i = 0; i < cipherSig.length; i++) 
					strCipherSig += Byte.toString(cipherSig[i]) + " ";				
				strCipherSig = new String(Base64.encode(strCipherSig.getBytes()));


				onProgressUpdate(70);

				if(mAccount.getAes256())
					cipherMessage = "BEGIN CIPHERTEXT: " +  new String(Base64.encode(cipherMessage.getBytes())) + " :END CIPHERTEXT" ;
				else
					cipherMessage = "BEGIN CIPHERTEXT: " + new String(Base64.encode(cipherMessage.getBytes()))  + " :END CIPHERTEXT";

				if(mAccount.getAes256() && mAccount.getEncryptSubject() && mSubjectView.getText().toString() != "")
					cipherMessage += "\n\n--Encrypted with Secure9 Mail for Android.";

				else if(mAccount.getEncryptSubject() && mSubjectView.getText().toString() != "")
					cipherMessage += "\n\n--Encrypted with Secure9 Mail for Android";

				else if(mAccount.getAes256())
					cipherMessage += "\n\nEncrypted with Secure9 Mail for Android.";

				else
					cipherMessage += "\n\nEncrypted with Secure9 Mail for Android";


				isEncrypted = true;
				onProgressUpdate(85);

				byte[] filler= "0".getBytes();
				Arrays.fill(key, filler[0]);
				onProgressUpdate(95);
				return "no error";


			}
			catch(Exception f) 
			{
				//Clear password
				if(localPassword != null)
					Arrays.fill(localPassword, '0');
				return(f.getLocalizedMessage());
			}
		}


		protected void onPostExecute(String error)
		{

			onProgressUpdate(100);
			pd.dismiss();


			if(!error.equals("no error"))
				Toast.makeText(MessageCompose.this, error, Toast.LENGTH_LONG).show();
			else
			{    		
				mMessageContentView.setText(cipherMessage);
				if(mAccount.getEncryptSubject())
					mSubjectView.setText(strCipherSubject);
				mSignatureView.setText("");
				mSignatureView.setEnabled(false); 
				mSignatureView.setFocusable(false);
				mMessageContentView.setEnabled(false);
				mMessageContentView.setFocusable(false);
				mSubjectView.setEnabled(false);
				mSubjectView.setFocusable(false);
				//Hide keyboard
				((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))  
				.hideSoftInputFromWindow(mMessageContentView.getWindowToken(), 0); 
			}

			isEncrypted = true;
			if(localPassword != null)
				Arrays.fill(localPassword, '0');


			if(autoSend)
			{
				autoSend();
				autoSend=false;		
			}

			if(hasAttachments)
				Toast.makeText(MessageCompose.this,"\t\t\t    WARNING: \nAttachments are not encrypted!", Toast.LENGTH_LONG).show();

			optionsMenu.findItem(R.id.encrypt).setTitle("Decrypt").setIcon(R.drawable.ic_menu_decrypt);
		}
	}


	/**
	 * onEncrypt is called when user selects Encrypt/Decrypt.
	 * If text is already encrypted, undo Encryption for further editing.
	 * If not encrypted yet, check for existing AES key.  If not found,
	 * prompt user for choice between new key setup or one time password.
	 * If password is selected, call getPassword() to set Encryption password, 
	 * then call encryptAction() to actually encrypt the message.
	 * encryptAction() will also be called with an existing stored key.
	 */
	private void verifyPin()
	{
		//  Hash.verifyPin not working right now.  Need to run verification in another thread
		//  or make all functions using it public so it can call the "pin matched" functions
		//  directly.... Not a good idea?
		//	if(Hash.verifyPin(MessageCompose.this, mAccount.getEmail(), "ComposeVerifyPin"))
		//		encryptWithKey("verifyPin");

		AlertDialog.Builder alert = new AlertDialog.Builder(this);
		alert.setTitle(R.string.verify_pin_dialog_title);
		alert.setMessage(R.string.verify_pin_dialog_text);

		// Set an EditText view to get user input 
		final EditText input = new EditText(this);
		input.setInputType(InputType.TYPE_CLASS_PHONE);
		input.setTransformationMethod

		(android.text.method.PasswordTransformationMethod.getInstance());
		alert.setView(input);

		alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int whichButton) {

				char[] pinFromFile = new char[input.getText().length()];
				for(int i = 0; i < input.getText().length(); i++)
					pinFromFile[i] = input.getText().charAt(i);

				FileInputStream fis;
				try 
				{
					fis = openFileInput((mAccount.getEmail().toLowerCase()) + MessageCompose.this.getString(R.string.legacy_pin_file_type));
					BufferedReader in = new BufferedReader(new InputStreamReader(fis));	
					char[] pin = in.readLine().toCharArray();

					if (Arrays.equals(pin, pinFromFile))		
						encryptWithKey("verifyPin");

					else
						Toast.makeText(MessageCompose.this,R.string.pin_incorrect,Toast.LENGTH_LONG).show();


					Arrays.fill(pin, '0');
					Arrays.fill(pinFromFile, '0');
				}
				catch (FileNotFoundException e) 
				{
					//No .pin found, try .pin2
					try 
					{
						fis = openFileInput((mAccount.getEmail().toLowerCase()) + MessageCompose.this.getString(R.string.hashed_pin_file_type));
						BufferedReader in = new BufferedReader(new InputStreamReader(fis));	
						char[] hashFromFile = in.readLine().toCharArray();
						String storedRand = Hash.getRandom(String.valueOf(hashFromFile), input.getText().length(), MessageCompose.this);
						String storedStatic = Hash.getStatic(String.valueOf(hashFromFile), input.getText().length(), MessageCompose.this);

						char[] inputPin = new char[input.getText().length()];
						for(int i = 0; i < input.getText().length(); i++)
							inputPin[i] = input.getText().charAt(i);

						char[] inputHash = Hash.verify(mAccount.getEmail(	), 
								inputPin, storedRand, storedStatic).toCharArray();

						if(Arrays.equals(inputHash, hashFromFile))
							encryptWithKey("verifyPin");	
						else
							Toast.makeText(MessageCompose.this,R.string.pin_incorrect,Toast.LENGTH_LONG).show();


						if(inputHash !=  null)
							Arrays.fill(inputHash, '0');
						if(hashFromFile !=  null)
							Arrays.fill(hashFromFile, '0');


					} 
					catch (FileNotFoundException e1) 
					{
						Toast.makeText(MessageCompose.this,R.string.pin_not_found,Toast.LENGTH_LONG).show();
					} 
					catch (IOException e1) 
					{
						Toast.makeText(MessageCompose.this,R.string.error_key_io,Toast.LENGTH_LONG).show();
					}

				} 
				catch (IOException e) 
				{
					Toast.makeText(MessageCompose.this,"Error reading PIN file",Toast.LENGTH_LONG).show();
				} 
			} 

		});

		alert.setNegativeButton(R.string.cancel_action, new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int whichButton) {
				// Canceled.

			}
		});

		alert.setIcon(R.drawable.pin);
		alert.show();
	}



	//Secure9
	public static void revokeKey(Context context, Address[] recipient, Account mAccount) throws MessagingException{

		MimeMessage diffie = new MimeMessage();
		diffie.addSentDate(new Date());		
		diffie.setFrom(new Address(mAccount.getEmail()));

		diffie.setRecipients(RecipientType.TO, recipient);
		diffie.setSubject(context.getString(R.string.subject_key_revocation));
		diffie.setHeader("X-User-Agent", "Secure9 Mail for Android");

		final String replyTo = mAccount.getEmail();
		diffie.setReplyTo(new Address[] { new Address(replyTo) });
		diffie.setInReplyTo(mAccount.getEmail());
		diffie.setReferences(mAccount.getEmail());

		String text = "This is an automated message from Secure9 Mail.  The sender of this message has revoked your shared key.  To complete revocation and delete your copy of this key, click Menu>Delete Key\n\n";		

		TextBody body = new TextBody("\r\n" + text);
		diffie.setBody(body);
		MessagingController.getInstance(null).sendMessage(mAccount, diffie, null);
	}

	//Send automated DiffieHellman reply with "User B"'s public key.
	public static void replyPublicKey(Context context, Address[] recipient, Account mAccount, DiffieHellman dh) throws MessagingException
	{
		String publicKey = dh.getPublicKey().toString();

		MimeMessage diffie = new MimeMessage();
		diffie.addSentDate(new Date());		
		diffie.setFrom(new Address(mAccount.getEmail()));

		diffie.setRecipients(RecipientType.TO, recipient);
		diffie.setSubject(context.getString(R.string.subject_key_reply));
		diffie.setHeader("X-User-Agent", "Secure9 Mail for Android");

		final String replyTo = mAccount.getEmail();
		diffie.setReplyTo(new Address[] { new Address(replyTo) });
		diffie.setInReplyTo(mAccount.getEmail());
		diffie.setReferences(mAccount.getEmail());


		//Diffie stuff here
		String text = "This is an automated message from Secure9 Mail.  The sender of this message is replying to your key request with their public key.  To complete key setup, click Menu>Finalize Key\n\n";
		text = text + "OO";
		text = text + publicKey	 + "OO";

		TextBody body = new TextBody("\r\n" + text);
		diffie.setBody(body);
		MessagingController.getInstance(null).sendMessage(mAccount, diffie, null);

	}

	//Send initial DiffieHellman info from "User A" to "User B".
	public static void sendDHKey(Context context, Address[] recipient, Account mAccount, DiffieHellman dh) throws MessagingException
	{
		String publicKey = dh.getPublicKey().toString();
		String modulus =  dh.getModulus().toString();
		String generator = dh.getGenerator().toString();

		MimeMessage diffie = new MimeMessage();
		diffie.addSentDate(new Date());		
		diffie.setFrom(new Address(mAccount.getEmail()));

		diffie.setRecipients(RecipientType.TO, recipient);
		diffie.setSubject(context.getString(R.string.subject_key_setup));
		diffie.setHeader("X-User-Agent", "Secure9 Mail for Android");

		final String replyTo = mAccount.getEmail();
		diffie.setReplyTo(new Address[] { new Address(replyTo) });
		diffie.setInReplyTo(mAccount.getEmail());
		diffie.setReferences(mAccount.getEmail());

		//Diffie stuff here
		String text = "This is an automated message from Secure9 Mail.  The sender of this message is requesting secure key setup.  To complete key setup, click Menu>Setup Key\n\n";
		text = text + "OO";											      
		text = text + modulus + "OO";
		text = text + generator + "OO";
		text = text + publicKey	 + "OO";

		TextBody body = new TextBody("\r\n" + text);
		diffie.setBody(body);
		MessagingController.getInstance(null).sendMessage(mAccount, diffie, null);
	}

	//Reads key from file and calls Encryption() or decrypt().
	//If pin verification is enabled, this is called from VerifyPin()
	//Otherwise, directly from onEncrypt().
	private void encryptWithKey(String calledBy)
	{
		char[] password;
		Address[] to= null;
		to = getAddresses(mToView);
		String recipient = Address.toAddress(to);

		try
		{
			FileInputStream fis = openFileInput((mAccount.getEmail() + recipient + 
					this.getString(R.string.complete_key_file_type)).toLowerCase());
			BufferedReader in = new BufferedReader(new InputStreamReader(fis));

			//Read key file as char[] only... Would be MUCH cleaner if we used strings. (but not safe for keying material)
			char[] keyChars = new char[2500];
			int r;
			int index = 0;
			while ((r = in.read()) != -1) 
			{				
				keyChars[index] = (char) r;
				index++;
			}
			

			//find third index of OO to get key
			int delimiterIndex = -1;
			int delimitersFound = 0;
			for(int i = 0; i < keyChars.length - 1; i++)
			{
				if(keyChars[i] == 'O' && keyChars[i++] == 'O')
				{
					delimitersFound++;
					if(delimitersFound == 3)
						delimiterIndex = i + 1; //+1 so index starts at actual key value rather than O.
				}
			}

			char[] key = new char[1500];
			int keyIndex = 0; //Index for actual key array
			boolean endOfKey = false;//True when loop reaches next OO.

			//Begin at keyChars index of 2nd 'OO'.
			try
			{
				for(int i = delimiterIndex; i < keyChars.length - 2; i++)
				{
					//Add  3rd through 4th OO key values to key char[]
					if(keyChars[i] != 'O' && !endOfKey)
					{
						key[keyIndex] =  keyChars[i];
						keyIndex++;
					}
					else
						endOfKey = true; //When loop reaches next OO, end of key is true.
				}
				if(keyChars != null)
					Arrays.fill(keyChars, '0');
			}
			catch(ArrayIndexOutOfBoundsException a)
			{
				Toast.makeText(this, a.getLocalizedMessage() + "out of bounds",Toast.LENGTH_LONG).show();
			}

			if(mAccount.getAes256())
			{
				//256 bit
				password = new char[32];
				for(int i = 0; i < 32; i++)
					password[i]=key[i];
			}

			else
			{
				//128 bit
				password = new char[16];
				for(int i = 0; i < 16; i++)
					password[i]=key[i];			
			}

			if(key != null)
				Arrays.fill(key, '0');
			in.close();

		} 
		catch (FileNotFoundException e) 
		{
			password = new char[1];
			Toast.makeText(MessageCompose.this,	"FILE NOT FOUND",Toast.LENGTH_LONG).show();
			return;

		} 
		catch (IOException e) 
		{
			//ignore
			password = new char[1];
			return;
		}

		//If setting is not "ask for pin when.. Encrypting AND decrypting", decrypt
		if(isEncrypted && !mAccount.getAskForPin().equals("2")
				|| isEncrypted && calledBy.equals("verifyPin"))
		{
			try 
			{
				decrypt(password);
			} 
			catch (UnsupportedEncodingException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
			catch (CryptoException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//If ask for pin setting requires decrypt verification, verify pin
		else if(isEncrypted && mAccount.getAskForPin().equals("2"))
			verifyPin();
		//If no pin requirements, encrypt
		else{
			new Encryption(password).execute();
		}
	}

	//Ask user if they want to encrypt or decrypt with a password or the stored key
	private void promptPasswordOrKey()
	{
		String alert;
		int icon;
		int title;
		int keyButton;

		EncryptionNewKey.remoteEmailSource = recipient;
		//Set up message strings - needs to be put into strings.xml
		if(Keys.keyExists(MessageCompose.this, mAccount.getEmail(), recipient))
		{
			alert = this.getString(R.string.key_found_prompt);
			icon = R.drawable.key_good;
			title = R.string.key_found_dialog_title;
			keyButton = R.string.key_found_use_key_btn;
		}
		else
		{
			alert = this.getString(R.string.key_not_found_prompt) + recipient;	
			icon = R.drawable.key_bad;
			title = R.string.key_not_found_dialog_title;
			keyButton = R.string.key_not_found_use_key_btn;
		}

		//Prompt user for choice between creating key or using password
		DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {	
				switch (which){
				// Action for user wanting to encrypt with one-time password.
				case DialogInterface.BUTTON_POSITIVE: 
					getPassword();
					break; 
					// Action for user wanting to set up new DH key for recipient
				case DialogInterface.BUTTON_NEGATIVE:

					//If key exists and user clicked key button - put this in a method
					//with the key exists code!
					Address[] to= null;
					to = getAddresses(mToView);
					String recipient = Address.toAddress(to);

					if(Keys.keyExists(MessageCompose.this, mAccount.getEmail(),recipient))
						//Get "ask for pin when" and call appropriate methods here
						if(mAccount.getAskForPin().equals("2"))
							verifyPin();						
						else
							encryptWithKey("onEncrypt");
					else if(!Keys.keyExists(MessageCompose.this, mAccount.getEmail(), recipient))
						EncryptionNewKey.actionNewKey(MessageCompose.this, mAccount,"compose", recipient);

					break;
				}
			}
		};

		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(alert).setPositiveButton(R.string.key_use_password_btn, dialogClickListener)
		.setNegativeButton(keyButton, dialogClickListener)
		.setIcon(icon).setTitle(title).show();
	}

	//User pressed the "encrypt" button (same button for Delete key, Setup key, etc - title just changes).
	private void onEncrypt() 
	{

		//Test for no recipient
		if (getAddresses(mToView).length == 0 && getAddresses(mCcView).length == 0 && getAddresses(mBccView).length == 0)
		{
			mToView.setError(getString(R.string.message_compose_error_no_recipients));
			Toast.makeText(this, getString(R.string.message_compose_error_no_recipients), Toast.LENGTH_LONG).show();
			return;
		}

		//Pull out recipient address for processing
		Address[] to= null;		
		to = getAddresses(mToView);		
		recipient = Address.toAddress(to);

		//if NO key exists or user selected always prompt, prompt user for choice  		
		if(!Keys.keyExists(MessageCompose.this, mAccount.getEmail(), recipient) || !mAccount.getUseKeyDefault())
			promptPasswordOrKey();

		else if(Keys.keyExists(MessageCompose.this, mAccount.getEmail(), recipient))
		{  
			if(mAccount.getAskForPin().equals("2"))
				verifyPin();			
			else
				encryptWithKey("onEncrypt");		
		}
		EncryptionNewKey.remoteEmailSource = "";
	}





	/**
	 * Kick off a picker for whatever kind of MIME types we'll accept and let Android take over.
	 */
	private void onAddAttachment()
	{
		if (Secure9.isGalleryBuggy())
		{
			if (Secure9.useGalleryBugWorkaround())
			{
				Toast.makeText(MessageCompose.this,
						getString(R.string.message_compose_use_workaround),
						Toast.LENGTH_LONG).show();
			}
			else
			{
				Toast.makeText(MessageCompose.this,
						getString(R.string.message_compose_buggy_gallery),
						Toast.LENGTH_LONG).show();
			}
		}

		onAddAttachment2(Secure9.ACCEPTABLE_ATTACHMENT_SEND_TYPES[0]);
	}

	/**
	 * Kick off a picker for the specified MIME type and let Android take over.
	 */
	private void onAddAttachment2(final String mime_type)
	{
		Intent i = new Intent(Intent.ACTION_GET_CONTENT);
		i.addCategory(Intent.CATEGORY_OPENABLE);
		i.setType(mime_type);
		startActivityForResult(Intent.createChooser(i, null), ACTIVITY_REQUEST_PICK_ATTACHMENT);
	}

	private void addAttachment(Uri uri)
	{
		addAttachment(uri, -1, null);
	}

	private void addAttachment(Uri uri, int size, String name)
	{
		ContentResolver contentResolver = getContentResolver();

		Attachment attachment = new Attachment();
		attachment.name = name;
		attachment.size = size;
		attachment.uri = uri;

		if (attachment.size == -1 || attachment.name == null)
		{
			Cursor metadataCursor = contentResolver.query(uri, new String[] { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE }, null, null, null);
			if (metadataCursor != null)
			{
				try
				{
					if (metadataCursor.moveToFirst())
					{
						if (attachment.name == null)
						{
							attachment.name = metadataCursor.getString(0);
						}
						if (attachment.size == -1)
						{
							attachment.size = metadataCursor.getInt(1);
							Log.v(Secure9.LOG_TAG, "size: " + attachment.size);
						}
					}
				}
				finally
				{
					metadataCursor.close();
				}
			}
		}

		if (attachment.name == null)
		{
			attachment.name = uri.getLastPathSegment();
		}

		String contentType = contentResolver.getType(uri);

		if (contentType == null)
		{
			contentType = MimeUtility.getMimeTypeByExtension(attachment.name);
		}

		attachment.contentType = contentType;

		if (attachment.size<=0)
		{
			String uriString = uri.toString();
			if (uriString.startsWith("file://"))
			{
				Log.v(Secure9.LOG_TAG, uriString.substring("file://".length()));
				File f = new File(uriString.substring("file://".length()));
				attachment.size = f.length();
			}
			else
			{
				Log.v(Secure9.LOG_TAG, "Not a file: " + uriString);
			}
		}
		else
		{
			Log.v(Secure9.LOG_TAG, "old attachment.size: " + attachment.size);
		}
		Log.v(Secure9.LOG_TAG, "new attachment.size: " + attachment.size);

		View view = getLayoutInflater().inflate(R.layout.message_compose_attachment, mAttachments, false);
		TextView nameView = (TextView)view.findViewById(R.id.attachment_name);
		ImageButton delete = (ImageButton)view.findViewById(R.id.attachment_delete);
		nameView.setText(attachment.name);
		delete.setOnClickListener(this);
		delete.setTag(view);
		view.setTag(attachment);
		mAttachments.addView(view);
		hasAttachments = true;
		if(isEncrypted || mAccount.getEncryptAll())
			Toast.makeText(MessageCompose.this,"\t\t\t    WARNING: \nAttachments are not encrypted!",Toast.LENGTH_LONG).show();
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		if (resultCode != RESULT_OK)
			return;
		if (data == null)
		{
			return;
		}
		switch (requestCode)
		{
		case ACTIVITY_REQUEST_PICK_ATTACHMENT:

			addAttachment(data.getData());
			mDraftNeedsSaving = true;
			break;
		case ACTIVITY_CHOOSE_IDENTITY:
			onIdentityChosen(data);
			break;
		}
	}

	private void onIdentityChosen(Intent intent)
	{
		Bundle bundle = intent.getExtras();;
		switchToIdentity((Identity)bundle.getSerializable(ChooseIdentity.EXTRA_IDENTITY));
	}

	private void switchToIdentity(Identity identity)
	{
		mIdentity = identity;
		mIdentityChanged = true;
		mDraftNeedsSaving = true;
		updateFrom();
		updateSignature();
	}

	private void updateFrom()
	{
		if (mIdentityChanged)
		{
			mFromView.setVisibility(View.VISIBLE);
		}
		mFromView.setText(getString(R.string.message_view_from_format, mIdentity.getName(), mIdentity.getEmail()));
	}

	private void updateSignature()
	{
		if (mIdentity.getSignatureUse())
		{
			mSignatureView.setText(mIdentity.getSignature());
			mSignatureView.setVisibility(View.VISIBLE);
		}
		else
		{
			mSignatureView.setVisibility(View.GONE);
		}
	}

	public void onClick(View view)
	{
		switch (view.getId())
		{
		case R.id.attachment_delete:
			/*
			 * The view is the delete button, and we have previously set the tag of
			 * the delete button to the view that owns it. We don't use parent because the
			 * view is very complex and could change in the future.
			 */
			mAttachments.removeView((View) view.getTag());
			mDraftNeedsSaving = true;
			hasAttachments = false;
			break;
		case R.id.quoted_text_delete:
			mQuotedTextBar.setVisibility(View.GONE);
			mQuotedText.setVisibility(View.GONE);
			mDraftNeedsSaving = true;
			break;
		}
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		switch (item.getItemId())
		{
		case R.id.send:
			onSend();
			break;
		case R.id.save:
			onSave();
			break;
		case R.id.discard:
			onDiscard();
			break;
		case R.id.add_cc_bcc:
			onAddCcBcc();
			break;
		case R.id.add_attachment:
			onAddAttachment();
			break;
		case R.id.add_attachment_image:
			onAddAttachment2("image/*");
			break;
		case R.id.add_attachment_video:
			onAddAttachment2("video/*");
			break;
		case R.id.choose_identity:
			onChooseIdentity();
			break;
		case R.id.encrypt:
			onEncrypt();
			break;
		default:
			return super.onOptionsItemSelected(item);
		}
		return true;
	}

	private void onChooseIdentity()
	{
		if (mAccount.getIdentities().size() > 1)
		{
			Intent intent = new Intent(this, ChooseIdentity.class);
			intent.putExtra(ChooseIdentity.EXTRA_ACCOUNT, mAccount.getUuid());
			startActivityForResult(intent, ACTIVITY_CHOOSE_IDENTITY);
		}
		else
		{
			Toast.makeText(this, getString(R.string.no_identities),
					Toast.LENGTH_LONG).show();
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		super.onCreateOptionsMenu(menu);
		optionsMenu = menu;
		if(mAccount.getEncryptAll())
			getMenuInflater().inflate(R.menu.autosend_message_compose_option, menu);
		else
			getMenuInflater().inflate(R.menu.message_compose_option, menu);

		/*
		 * Show the menu items "Add attachment (Image)" and "Add attachment (Video)"
		 * if the work-around for the Gallery bug is enabled (see Issue 1186).
		 */
		int found = 0;
		for (int i = menu.size() - 1; i >= 0; i--)
		{
			MenuItem item = menu.getItem(i);
			int id = item.getItemId();
			if ((id == R.id.add_attachment_image) ||
					(id == R.id.add_attachment_video))
			{
				item.setVisible(Secure9.useGalleryBugWorkaround());
				found++;
			}

			// We found all the menu items we were looking for. So stop here.
			if (found == 2) break;
		}
		MenuItem item = menu.findItem(R.id.encrypt);
		if(isEncrypted)
			item.setTitle("Decrypt");		
		else
			item.setTitle("Encrypt");		



		return true;
	}

	/**
	 * Returns true if all attachments were able to be attached, otherwise returns false.
	 */
	private boolean loadAttachments(Part part, int depth) throws MessagingException
	{
		if (part.getBody() instanceof Multipart)
		{
			Multipart mp = (Multipart) part.getBody();
			boolean ret = true;
			for (int i = 0, count = mp.getCount(); i < count; i++)
			{
				if (!loadAttachments(mp.getBodyPart(i), depth + 1))
				{
					ret = false;
				}
			}
			return ret;
		}
		else
		{
			String contentType = MimeUtility.unfoldAndDecode(part.getContentType());
			String name = MimeUtility.getHeaderParameter(contentType, "name");
			if (name != null)
			{
				Body body = part.getBody();
				if (body != null && body instanceof LocalAttachmentBody)
				{
					final Uri uri = ((LocalAttachmentBody) body).getContentUri();
					mHandler.post(new Runnable()
					{
						public void run()
						{
							addAttachment(uri);
						}
					});
				}
				else
				{
					return false;
				}
			}
			return true;
		}
	}

	/**
	 * Pull out the parts of the now loaded source message and apply them to the new message
	 * depending on the type of message being composed.
	 * @param message
	 */
	private void processSourceMessage(Message message)
	{
		String action = getIntent().getAction();
		if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action))
		{
			try
			{
				if (message.getSubject() != null && !message.getSubject().toLowerCase().startsWith("re:"))
				{
					mSubjectView.setText("Re: " + message.getSubject());
				}
				else
				{
					mSubjectView.setText(message.getSubject());
				}
				/*
				 * If a reply-to was included with the message use that, otherwise use the from
				 * or sender address.
				 */
				Address[] replyToAddresses;
				if (message.getReplyTo().length > 0)
				{
					addAddresses(mToView, replyToAddresses = message.getReplyTo());
				}
				else
				{
					addAddresses(mToView, replyToAddresses = message.getFrom());
				}

				if (message.getMessageId() != null && message.getMessageId().length() > 0)
				{
					String messageId = message.getMessageId();
					mInReplyTo = messageId;

					if (message.getReferences() != null && message.getReferences().length > 0)
					{
						StringBuffer buffy = new StringBuffer();
						for (int i=0; i < message.getReferences().length; i++)
							buffy.append(message.getReferences()[i]);

						mReferences = buffy.toString() + " " + mInReplyTo;
					}
					else
					{
						mReferences = mInReplyTo;
					}

				}
				else
				{
					if (Secure9.DEBUG)
						Log.d(Secure9.LOG_TAG, "could not get Message-ID.");
				}

				Part part = MimeUtility.findFirstPartByMimeType(mSourceMessage,
				"text/plain");
				if (part != null)
				{
					String quotedText = String.format(
							getString(R.string.message_compose_reply_header_fmt),
							Address.toString(mSourceMessage.getFrom()));

					final String prefix = mAccount.getQuotePrefix();
					// "$" and "\" in the quote prefix have to be escaped for
					// the replaceAll() invocation.
					final String escapedPrefix = prefix.replaceAll("(\\\\|\\$)", "\\\\$1");
					quotedText += MimeUtility.getTextFromPart(part).replaceAll(
							"(?m)^", escapedPrefix);

					quotedText = quotedText.replaceAll("\\\r", "");
					mQuotedText.setText(quotedText);

					mQuotedTextBar.setVisibility(View.VISIBLE);
					mQuotedText.setVisibility(View.VISIBLE);
				}

				if (ACTION_REPLY_ALL.equals(action) || ACTION_REPLY.equals(action))
				{
					Identity useIdentity = null;
					for (Address address : message.getRecipients(RecipientType.TO))
					{
						Identity identity = mAccount.findIdentity(address);
						if (identity != null)
						{
							useIdentity = identity;
							break;
						}
					}
					if (useIdentity == null)
					{
						if (message.getRecipients(RecipientType.CC).length > 0)
						{
							for (Address address : message.getRecipients(RecipientType.CC))
							{
								Identity identity = mAccount.findIdentity(address);
								if (identity != null)
								{
									useIdentity = identity;
									break;
								}
							}
						}
					}
					if (useIdentity != null)
					{
						Identity defaultIdentity = mAccount.getIdentity(0);
						if (useIdentity != defaultIdentity)
						{
							switchToIdentity(useIdentity);
						}
					}
				}

				if (ACTION_REPLY_ALL.equals(action))
				{
					for (Address address : message.getRecipients(RecipientType.TO))
					{
						if (!mAccount.isAnIdentity(address))
						{
							addAddress(mToView, address);
						}

					}
					if (message.getRecipients(RecipientType.CC).length > 0)
					{
						for (Address address : message.getRecipients(RecipientType.CC))
						{
							if (!mAccount.isAnIdentity(address) && !Utility.arrayContains(replyToAddresses, address))
							{
								addAddress(mCcView, address);
							}

						}
						mCcView.setVisibility(View.VISIBLE);
					}
				}
			}
			catch (MessagingException me)
			{
				/*
				 * This really should not happen at this point but if it does it's okay.
				 * The user can continue composing their message.
				 */
			}
		}
		else if (ACTION_FORWARD.equals(action))
		{
			try
			{
				if (message.getSubject() != null && !message.getSubject().toLowerCase().startsWith("fwd:"))
				{
					mSubjectView.setText("Fwd: " + message.getSubject());
				}
				else
				{
					mSubjectView.setText(message.getSubject());
				}
				Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
				if (part == null)
				{
					part = MimeUtility.findFirstPartByMimeType(message, "text/html");
				}
				if (part != null)
				{
					String quotedText = MimeUtility.getTextFromPart(part);
					if (quotedText != null)
					{
						String text = String.format(
								getString(R.string.message_compose_fwd_header_fmt),
								mSourceMessage.getSubject(),
								Address.toString(mSourceMessage.getFrom()),
								Address.toString(
										mSourceMessage.getRecipients(RecipientType.TO)),
										Address.toString(
												mSourceMessage.getRecipients(RecipientType.CC)));
						quotedText = quotedText.replaceAll("\\\r", "");
						text += quotedText;
						mQuotedText.setText(text);
						mQuotedTextBar.setVisibility(View.VISIBLE);
						mQuotedText.setVisibility(View.VISIBLE);
					}
				}
				if (!mSourceMessageProcessed)
				{
					if (!loadAttachments(message, 0))
					{
						mHandler.sendEmptyMessage(MSG_SKIPPED_ATTACHMENTS);
					}
				}
			}
			catch (MessagingException me)
			{
				/*
				 * This really should not happen at this point but if it does it's okay.
				 * The user can continue composing their message.
				 */
			}
		}
		else if (ACTION_EDIT_DRAFT.equals(action))
		{
			try
			{
				mDraftUid = message.getUid();
				mSubjectView.setText(message.getSubject());
				addAddresses(mToView, message.getRecipients(RecipientType.TO));
				if (message.getRecipients(RecipientType.CC).length > 0)
				{
					addAddresses(mCcView, message.getRecipients(RecipientType.CC));
					mCcView.setVisibility(View.VISIBLE);
				}
				if (message.getRecipients(RecipientType.BCC).length > 0)
				{
					addAddresses(mBccView, message.getRecipients(RecipientType.BCC));
					mBccView.setVisibility(View.VISIBLE);
				}

				// Read In-Reply-To header from draft
				final String[] inReplyTo = message.getHeader("In-Reply-To");
				if ((inReplyTo != null) && (inReplyTo.length >= 1))
				{
					mInReplyTo = inReplyTo[0];
				}

				// Read References header from draft
				final String[] references = message.getHeader("References");
				if ((references != null) && (references.length >= 1))
				{
					mReferences = references[0];
				}

				if (!mSourceMessageProcessed)
				{
					loadAttachments(message, 0);
				}
				Integer bodyLength = null;
				String[] secure9identities = message.getHeader(Secure9.K9MAIL_IDENTITY);
				if (secure9identities != null && secure9identities.length > 0)
				{
					String secure9identity = secure9identities[0];

					if (secure9identity != null)
					{
						if (Secure9.DEBUG)
							Log.d(Secure9.LOG_TAG, "Got a saved identity: " + secure9identity);
						StringTokenizer tokens = new StringTokenizer(secure9identity, ":", false);

						String bodyLengthS = null;
						String name = null;
						String email = null;
						String signature = null;
						boolean signatureUse = message.getFolder().getAccount().getSignatureUse();
						if (tokens.hasMoreTokens())
						{
							bodyLengthS = Utility.base64Decode(tokens.nextToken());
							try
							{
								bodyLength = Integer.parseInt(bodyLengthS);
							}
							catch (Exception e)
							{
								Log.e(Secure9.LOG_TAG, "Unable to parse bodyLength '" + bodyLengthS + "'");
							}
						}
						if (tokens.hasMoreTokens())
						{
							signatureUse = true;
							signature = Utility.base64Decode(tokens.nextToken());
						}
						if (tokens.hasMoreTokens())
						{
							name = Utility.base64Decode(tokens.nextToken());
						}
						if (tokens.hasMoreTokens())
						{
							email = Utility.base64Decode(tokens.nextToken());
						}

						Identity newIdentity = new Identity();
						newIdentity.setSignatureUse(signatureUse);
						if (signature != null)
						{
							newIdentity.setSignature(signature);
							mSignatureChanged = true;
						}
						else
						{
							newIdentity.setSignature(mIdentity.getSignature());
						}

						if (name != null)
						{
							newIdentity.setName(name);
							mIdentityChanged = true;
						}
						else
						{
							newIdentity.setName(mIdentity.getName());
						}

						if (email != null)
						{
							newIdentity.setEmail(email);
							mIdentityChanged = true;
						}
						else
						{
							newIdentity.setEmail(mIdentity.getEmail());
						}

						mIdentity = newIdentity;

						updateSignature();
						updateFrom();

					}
				}
				Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
				if (part != null)
				{
					String text = MimeUtility.getTextFromPart(part);
					if (bodyLength != null && bodyLength + 1 < text.length())   // + 1 to get rid of the newline we added when saving the draft
					{
						String bodyText = text.substring(0, bodyLength);
						String quotedText = text.substring(bodyLength + 1, text.length());

						mMessageContentView.setText(bodyText);
						mQuotedText.setText(quotedText);

						mQuotedTextBar.setVisibility(View.VISIBLE);
						mQuotedText.setVisibility(View.VISIBLE);
					}
					else
					{
						mMessageContentView.setText(text);
					}
				}
			}
			catch (MessagingException me)
			{
				// TODO
			}
		}
		mSourceMessageProcessed = true;
		mDraftNeedsSaving = false;
	}

	class Listener extends MessagingListener
	{
		@Override
		public void loadMessageForViewStarted(Account account, String folder, String uid)
		{
			if (mSourceMessageUid==null
					|| !mSourceMessageUid.equals(uid))
			{
				return;
			}

			mHandler.sendEmptyMessage(MSG_PROGRESS_ON);
		}

		@Override
		public void loadMessageForViewFinished(Account account, String folder, String uid, Message message)
		{
			if (mSourceMessageUid==null
					|| !mSourceMessageUid.equals(uid))
			{
				return;
			}

			mHandler.sendEmptyMessage(MSG_PROGRESS_OFF);
		}

		@Override
		public void loadMessageForViewBodyAvailable(Account account, String folder, String uid, final Message message)
		{
			if (mSourceMessageUid==null
					|| !mSourceMessageUid.equals(uid))
			{
				return;
			}

			mSourceMessage = message;
			runOnUiThread(new Runnable()
			{
				public void run()
				{
					processSourceMessage(message);
				}
			});
		}

		@Override
		public void loadMessageForViewFailed(Account account, String folder, String uid, Throwable t)
		{
			if (mSourceMessageUid==null
					|| !mSourceMessageUid.equals(uid))
			{
				return;
			}
			mHandler.sendEmptyMessage(MSG_PROGRESS_OFF);
			// TODO show network error
		}

		@Override
		public void messageUidChanged(Account account, String folder, String oldUid, String newUid)
		{
			if (account.equals(mAccount) && folder.equals(mAccount.getDraftsFolderName()))
			{
				if (oldUid.equals(mDraftUid))
				{
					mDraftUid = newUid;
				}
			}

			if (account.equals(mAccount) && (folder.equals(mFolder)))
			{
				if (oldUid.equals(mSourceMessageUid))
				{
					mSourceMessageUid = newUid;
				}
				if (mSourceMessage != null && (oldUid.equals(mSourceMessage.getUid())))
				{
					mSourceMessage.setUid(newUid);
				}
			}
		}
	}

	private String decode(String s)
	throws UnsupportedEncodingException
	{
		return URLDecoder.decode(s, "UTF-8");
	}

	/**
	 * When we are launched with an intent that includes a mailto: URI, we can actually
	 * gather quite a few of our message fields from it.
	 *
	 * @mailToString the href (which must start with "mailto:").
	 */
	private void initializeFromMailTo(String mailToString)
	{

		// Chop up everything between mailto: and ? to find recipients
		int index = mailToString.indexOf("?");
		int length = "mailto".length() + 1;
		String to;
		try
		{
			// Extract the recipient after mailto:
			if (index == -1)
			{
				to = decode(mailToString.substring(length));
			}
			else
			{
				to = decode(mailToString.substring(length, index));
			}
			mToView.setText(to);
		}
		catch (UnsupportedEncodingException e)
		{
			Log.e(Secure9.LOG_TAG, e.getMessage() + " while decoding '" + mailToString + "'");
		}

		// Extract the other parameters

		// We need to disguise this string as a URI in order to parse it
		Uri uri = Uri.parse("foo://" + mailToString);

		String addressList;

		addressList = "";
		List<String> cc = uri.getQueryParameters("cc");
		for (String address : cc)
		{
			addressList += address + ",";
		}
		mCcView.setText(addressList);

		addressList = "";
		List<String> bcc = uri.getQueryParameters("bcc");
		for (String address : bcc)
		{
			addressList += address + ",";
		}
		mBccView.setText(addressList);

		List<String> subject = uri.getQueryParameters("subject");
		if (subject.size() > 0)
		{
			mSubjectView.setText(subject.get(0));
		}

		List<String> body = uri.getQueryParameters("body");
		if (body.size() > 0)
		{
			mMessageContentView.setText(body.get(0));
		}
	}

	private String parseTempSignature(String message) 
	{
		return message.substring(0, message.length() - (getTempSignature().length()));
	}

	public void setTempSignature(String tempSignature) {
		this.tempSignature = tempSignature;
	}

	public String getTempSignature() {
		return tempSignature;
	}

}
